package com.bai.process.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bai.Springsecurity.custom.LoginUserInfoHelper;
import com.bai.model.process.Process;
import com.bai.model.process.ProcessRecord;
import com.bai.model.process.ProcessTemplate;
import com.bai.model.system.SysUser;
import com.bai.model.vo.process.ApprovalVo;
import com.bai.model.vo.process.ProcessFormVo;
import com.bai.model.vo.process.ProcessQueryVo;
import com.bai.model.vo.process.ProcessVo;
import com.bai.process.mapper.OaProcessMapper;
import com.bai.process.service.OaProcessRecordService;
import com.bai.process.service.OaProcessService;
import com.bai.process.service.OaProcessTemplateService;
import com.bai.serviceOA.service.SysUserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.activiti.bpmn.model.*;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * <p>
 * 审批类型 服务实现类
 * </p>
 *
 * @author bai
 * @since 2023-06-01
 */
@Service
public class OaProcessServiceImpl extends ServiceImpl<OaProcessMapper, Process> implements OaProcessService {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private SysUserService userService;
    @Autowired
    private OaProcessTemplateService processTemplateService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private OaProcessRecordService processRecordService;
    @Autowired
    private HistoryService historyService;

    //审批管理列表
    @Override
    public IPage<ProcessVo> selectPage(Page<ProcessVo> pageParam, ProcessQueryVo processQueryVo) {
        IPage<ProcessVo> pageModel = baseMapper.selectPage(pageParam, processQueryVo);
        return pageModel;
    }

    //部署流程定义
    @Override
    public void deployByZip(String deployPath) {
        InputStream resourceAsStream = this
                .getClass()
                .getClassLoader()
                .getResourceAsStream(deployPath);
        ZipInputStream zipInputStream = new ZipInputStream(resourceAsStream);
        //流程部署
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .deploy();
    }

    /**
     * 启动流程部署
     *
     * @param processFormVo
     */
    @Override
    public void startUp(ProcessFormVo processFormVo) {
        //TODO:根据当前用户id获取用户信息
        SysUser user = userService.getById(LoginUserInfoHelper.getUserId());
        //TODO:根据审批模板id获取模板信息
        ProcessTemplate processTemplate = processTemplateService.getById(processFormVo.getProcessTemplateId());
        //TODO:保存提交的审批提交信息
        Process process = new Process();
        //processForVo富孩子到process中
        BeanUtils.copyProperties(processFormVo, process);
        process.setStatus(1);//审批中
        String workNo = System.currentTimeMillis() + "";
        process.setProcessCode(workNo);
        process.setUserId(LoginUserInfoHelper.getUserId());
        process.setFormValues(processFormVo.getFormValues());
        process.setTitle(user.getName() + "发起" + processTemplate.getName() + "申请");
        baseMapper.insert(process);
        //TODO:启动流程实例   RuntimeService
        //流程定义的key
        String processDefinitionKey = processTemplate.getProcessDefinitionKey();
        //业务key
        String businessKey = String.valueOf(process.getId());
        //流程参数 from表单json数据，转为map
        String formValues = processFormVo.getFormValues();
        JSONObject jsonObject = JSON.parseObject(formValues);
        JSONObject formData = jsonObject.getJSONObject("formData");
        HashMap<String, Object> map = new HashMap<>();
        for (Map.Entry<String, Object> entry : formData.entrySet()) {
            map.put(entry.getKey(), entry.getValue());
        }
        HashMap<String, Object> variables = new HashMap<>();
        variables.put("data", map);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey,
                businessKey,
                variables);
        //TODO:查询下一个审批人
        List<Task> list = this.getCurrentTaskList(processInstance.getId());
        ArrayList<String> nameList = new ArrayList<>();
        for (Task task : list) {
            String assigneeName = task.getAssignee();
            SysUser assigneeUser = userService.getUserByUserName(assigneeName);
            String name = assigneeUser.getName();
            nameList.add(name);
            //TODO:推送消息
        }
        //TODO:业务和流程关联
        process.setProcessInstanceId(processInstance.getId());
        process.setDescription("等待" + StringUtils.join(nameList.toArray(), ",") + "审批");
        baseMapper.updateById(process);

        //TODO:记录操作审批信息记录
        processRecordService.record(process.getId(), 1, "发起申请");
    }

    /**
     * 查询待处理任务列表
     *
     * @param pageParam
     * @return
     */
    @Override
    public IPage<ProcessVo> findPending(Page<Process> pageParam) {
        //TODO:根据当前登录用户查询
        TaskQuery query = taskService.createTaskQuery()
                .taskAssignee(LoginUserInfoHelper.getUsername())
                .orderByTaskCreateTime()
                .desc();
        //TODO:分页,返回List列表
        //listPage()参数：开始位置   每页显示记录数
        int begin = (int) ((pageParam.getCurrent() - 1) * pageParam.getSize());
        int size = (int) (pageParam.getSize());
        List<Task> taskList = query.listPage(begin, size);
        //查询总记录数
        long count = query.count();
        //TODO:封装list到ProcessVo里
        List<ProcessVo> processVoList = new ArrayList<>();
        for (Task task : taskList) {
            //从task获取流程实例id
            String processInstanceId = task.getProcessInstanceId();
            //根据流程实例id获取实例对象
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();
            //从流程实例对象获取业务key---->就是processId
            String businessKey = processInstance.getBusinessKey();
            if (businessKey == null) {
                continue;
            }
            //根据业务key获取Process对象
            long processId = Long.parseLong(businessKey);
            Process process = baseMapper.selectById(processId);
            //从Process对象复制ProcessVo对象
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process, processVo);
            processVo.setTaskId(task.getId());
            processVoList.add(processVo);
        }
        //TODO:封装返回的page对象
        IPage<ProcessVo> page = new Page<>(pageParam.getCurrent(), pageParam.getSize(), count);
        page.setRecords(processVoList);
        return page;
    }

    /**
     * 查询审批详细信息
     *
     * @param id
     * @return
     */
    @Override
    public Map<String, Object> show(Long id) {
        //TODO:根据流程id获取流程信息Process
        Process process = baseMapper.selectById(id);
        //TODO:根据流程id获取流程记录信息
        LambdaQueryWrapper<ProcessRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProcessRecord::getProcessId, id);
        List<ProcessRecord> processRecordList = processRecordService.list(wrapper);
        //TODO:根据模板id查询模板信息
        ProcessTemplate template = processTemplateService.getById(process.getProcessTemplateId());
        //TODO:判断当前用户是否可以进行审批
        boolean isApprove = false;
        List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
        for (Task task : taskList) {
            //判断任务当前审批人是否为当前用户
            String username = LoginUserInfoHelper.getUsername();
            if (task.getAssignee().equals(username)) {
                isApprove = true;
            }
        }
        //TODO:封装map
        Map<String, Object> map = new HashMap<>();
        map.put("process", process);
        map.put("processRecordList", processRecordList);
        map.put("processTemplate", template);
        map.put("isApprove", isApprove);
        return map;
    }

    /**
     * 审批
     *
     * @param approvalVo
     */
    @Override
    public void approve(ApprovalVo approvalVo) {
        //TODO:获取任务id，根据id获取流程变量
        String taskId = approvalVo.getTaskId();
        Map<String, Object> variables = taskService.getVariables(taskId);
        for (Map.Entry<String, Object> entry : variables.entrySet()) {
            System.out.println(entry.getKey());
            System.out.println(entry.getValue());
        }
        //TODO:判断状态值
        if (approvalVo.getStatus() == 1) {
            //状态值 1 通过
            taskService.complete(taskId);
        } else {
            // 状态值 -1 直接返回
            this.endTask(taskId);
        }
        //记录审批信息
        String description = approvalVo.getStatus().intValue() == 1 ? "已通过" : "驳回";
        processRecordService.record(approvalVo.getProcessId(),
                approvalVo.getStatus(),
                description);
        //TODO:查询下一个审批人,更新流程表记录
        Process process = baseMapper.selectById(approvalVo.getProcessId());
        //查询任务
        List<Task> taskList = this.getCurrentTaskList(process.getProcessInstanceId());
        if (!CollectionUtils.isEmpty(taskList)) {
            List<String> assignList = new ArrayList<>();
            for (Task task : taskList) {
                String assignee = task.getAssignee();
                SysUser user = userService.getUserByUserName(assignee);
                assignList.add(user.getUsername());

                //TODO:消息推送
            }
            //更新
            process.setDescription("等待" + StringUtils.join(assignList.toArray(), ",") + "审批");
            process.setStatus(1);
        } else {
            if (approvalVo.getStatus().intValue() == 1) {
                process.setDescription("审批完成(通过)");
                process.setStatus(2);
            } else {
                process.setDescription("审批完成(驳回)");
                process.setStatus(-1);
            }
        }

    }

    /**
     * 已处理
     *
     * @param pageParam
     * @return
     */
    @Override
    public IPage<ProcessVo> findProcessed(Page<Process> pageParam) {
        //封装查询条件
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(LoginUserInfoHelper.getUsername())
                .finished()
                .orderByTaskCreateTime()
                .desc();
        //开始位置
        int begin = (int) ((pageParam.getCurrent() - 1) * pageParam.getSize());
        //每页显示的记录数
        int size = (int) (pageParam.getSize());
        List<HistoricTaskInstance> list = query.listPage(begin, size);
        long count = query.count();
        List<ProcessVo> processVoArrayList = new ArrayList<>();
        for (HistoricTaskInstance item : list) {
            //流程实例id
            String processInstanceId = item.getProcessInstanceId();
            LambdaQueryWrapper<Process> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Process::getProcessInstanceId, processInstanceId);
            Process process = baseMapper.selectOne(wrapper);
            //process ---> processVo
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process, processVo);
            processVoArrayList.add(processVo);
        }
        IPage<ProcessVo> pageModel = new Page<ProcessVo>(pageParam.getCurrent(), pageParam.getSize(), count);
        pageModel.setRecords(processVoArrayList);
        return pageModel;
    }

    /**
     * 已发起
     * @param pageParam
     * @return
     */
    @Override
    public IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam) {
        ProcessQueryVo processQueryVo = new ProcessQueryVo();
        processQueryVo.setUserId(LoginUserInfoHelper.getUserId());
        IPage<ProcessVo> processVoIPage = baseMapper.selectPage(pageParam, processQueryVo);
        return processVoIPage;
    }

    /**
     * 结束流程
     *
     * @param taskId
     */
    private void endTask(String taskId) {
        //根据任务id获取任务对象 task
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        //获取流程定义模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        //获取结束流向节点
        List<EndEvent> endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
        if (CollectionUtils.isEmpty(endEventList)) {
            return;
        }
        FlowNode endFlowNode = (FlowNode) endEventList.get(0);
        //当前的流程节点
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
        //临时保存当前活动的原始放向
        List originalSequenceFlowLiat = new ArrayList<>();
        originalSequenceFlowLiat.addAll(currentFlowNode.getOutgoingFlows());
        //清理当前流动方向
        currentFlowNode.getOutgoingFlows().clear();
        //创建新的流向
        SequenceFlow sequenceFlow = new SequenceFlow();
        sequenceFlow.setId("sequenceFlow");
        sequenceFlow.setSourceFlowElement(currentFlowNode);
        sequenceFlow.setTargetFlowElement(endFlowNode);
        //当前节点指向新的放向
        List newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(sequenceFlow);
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);
        //完成当前任务
        taskService.complete(taskId);
    }

    /**
     * 获取当前任务列表
     *
     * @param id
     * @return
     */
    private List<Task> getCurrentTaskList(String id) {
        List<Task> list = taskService.createTaskQuery().processInstanceId(id).list();
        return list;
    }
}
